package com.example.bluetoothchat.utils;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import com.example.bluetoothchat.ui.MainActivity;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.UUID;

public class ChatUtils {
    private Context mContext;
    private Handler mHandler;
    private BluetoothAdapter bluetoothAdapter;
    private ConnectionThread connectionThread;
    private AcceptThread acceptThread;
    private ConnectedThread connectedThread;

    private int state;
    private final String AppName = "BlueToothChartApp";
    private final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

    public static final int STATE_NONE = 0;
    public static final int STATE_LISTEN = 1;
    public static final int STATE_CONNECTING = 2;
    public static final int STATE_CONNECTED = 3;

    public ChatUtils(Context context,Handler handler){
        mContext = context;
        mHandler = handler;
        bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        state = STATE_NONE;
    }

    public synchronized void setState(int state) {
        this.state = state;
        mHandler.obtainMessage(MainActivity.MESSAGE_STATE_CHANGE,state,-1).sendToTarget();
    }

    public int getState() {
        return state;
    }

    private synchronized void start(){
        if (connectionThread != null){
            connectionThread.cancel();
            connectionThread = null;
        }

        if (acceptThread == null){
            acceptThread = new AcceptThread();
            acceptThread.start();
        }

        if (connectedThread != null){
            connectedThread.cancel();
            connectedThread = null;
        }

        setState(STATE_LISTEN);
    }

    public synchronized void stop(){
        if (connectionThread != null){
            connectionThread.cancel();
            connectionThread = null;
        }

        if (acceptThread != null){
            acceptThread.cancel();
            acceptThread = null;
        }

        if (connectedThread != null){
            connectedThread.cancel();
            connectedThread = null;
        }

        setState(STATE_NONE);
    }

    public void connect(BluetoothDevice device){
        if (state == STATE_CONNECTING){
            connectionThread.cancel();
            connectionThread = null;
        }

        connectionThread = new ConnectionThread(device);
        connectionThread.start();

        if (connectedThread != null){
            connectedThread.cancel();
            connectedThread = null;
        }

        state = STATE_CONNECTING;
    }

    public void write(byte[] bytes){
        ConnectedThread connThread;

        synchronized (this){
            if (state != STATE_CONNECTED) return;
            connThread = connectedThread;
        }

        connThread.write(bytes);
    }

    private class ConnectedThread extends Thread{
        private BluetoothSocket socket;
        private OutputStream outputStream;
        private InputStream inputStream;

        public ConnectedThread(BluetoothSocket socket){
            this.socket = socket;

            OutputStream os = null;
            InputStream is = null;

            try {
                os = socket.getOutputStream();
                is = socket.getInputStream();
            } catch (IOException e) {
                e.printStackTrace();
            }

            outputStream = os;
            inputStream = is;
        }

        @Override
        public void run() {
            byte[] buffer = new byte[1024];
            int bytes;

            try {
                bytes = inputStream.read(buffer);
                mHandler.obtainMessage(MainActivity.MESSAGE_READ,bytes,-1,buffer).sendToTarget();
            } catch (IOException e) {
                e.printStackTrace();
                connectionLost();
            }
        }

        public void write(byte[] bytes){
            try {
                outputStream.write(bytes);
                mHandler.obtainMessage(MainActivity.MESSAGE_WRITE,-1,-1,bytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        public void cancel(){
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void connectionLost(){
        Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(MainActivity.TOAST,"连接断开");
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        ChatUtils.this.start();
    }

    private class AcceptThread extends Thread{
        private BluetoothServerSocket bluetoothServerSocket;

        public AcceptThread(){
            BluetoothServerSocket serverSocket = null;
            try {
                serverSocket = bluetoothAdapter.listenUsingRfcommWithServiceRecord(AppName,uuid);
            } catch (IOException e) {
                e.printStackTrace();
            }

            bluetoothServerSocket = serverSocket;
        }

        @Override
        public void run() {
            BluetoothSocket bluetoothSocket = null;

            try {
                bluetoothSocket = bluetoothServerSocket.accept();
            } catch (IOException e) {
                e.printStackTrace();
                try {
                    bluetoothSocket.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
            }

            if (bluetoothSocket != null){
                switch (state){
                    case STATE_LISTEN:
                    case STATE_CONNECTING:
                        connected(bluetoothSocket,bluetoothSocket.getRemoteDevice());
                        break;
                    case STATE_NONE:
                    case STATE_CONNECTED:
                        try {
                            bluetoothSocket.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        break;
                }
            }
        }

        public void cancel(){
            try {
                bluetoothServerSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private class ConnectionThread extends Thread{
        private final BluetoothSocket bluetoothSocket;
        private final BluetoothDevice bluetoothDevice;

        public ConnectionThread(BluetoothDevice device){
            bluetoothDevice = device;

            BluetoothSocket socket = null;
            try {
                socket = device.createRfcommSocketToServiceRecord(uuid);
            } catch (IOException e) {
                e.printStackTrace();
            }

            bluetoothSocket = socket;
        }

        @Override
        public void run() {
            try {
                bluetoothSocket.connect();
            } catch (IOException e) {
                try {
                    bluetoothSocket.close();
                } catch (IOException ioException) {
                    ioException.printStackTrace();
                }
                e.printStackTrace();
                connectionFailed();
                return;
            }

            synchronized (ChatUtils.this){
                connectionThread = null;
            }

            connected(bluetoothSocket,bluetoothDevice);
        }

        public void cancel(){
            try {
                bluetoothSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void connectionFailed(){
        Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_TOAST);
        Bundle bundle = new Bundle();
        bundle.putString(MainActivity.TOAST,"Connection Failed");
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        ChatUtils.this.start();
    }

    private synchronized void connected(BluetoothSocket socket,BluetoothDevice device){
        if (connectionThread != null){
            connectionThread.cancel();
            connectionThread = null;
        }

        if (connectedThread != null){
            connectedThread.cancel();
            connectedThread = null;
        }

        connectedThread = new ConnectedThread(socket);
        connectedThread.start();

        Message msg = mHandler.obtainMessage(MainActivity.MESSAGE_DEVICE_NAME);
        Bundle bundle = new Bundle();
        bundle.putString(MainActivity.DEVICE_NAME,device.getName());
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        setState(STATE_CONNECTED);
    }
}
